﻿// Shader-Based-Image-Processing (SBIP)
// http://code.google.com/p/sbip/
//
// Copyright © Frank Nagl, 2009-2011
// admin@franknagl.de
//
namespace SBIP.Helper
{
    using System;
    using System.Drawing;   
    using SBIP.Helper.ColorSpace;

    /// <summary>
    /// Container for RGB pixel's 2d .Net image coordinates and its color.
    /// </summary>
    public class PixelRGBA
    {
        /// <summary>Alpha channel of pixel's color.</summary>
        public byte A { get; set; }
        /// <summary>Blue channel of pixel's color.</summary>
        public byte B { get; set; }
        /// <summary>RGB channels as <see cref="Color"/>.</summary>
        public Color Color
        {
            get { return Color.FromArgb(A, R, G, B); }
            // set { R = value.R; G = value.G; B = value.B; }
        }
        /// <summary>Green channel of pixel's color.</summary>
        public byte G { get; set; }
        /// <summary>X- and Y-coordinate as <see cref="Point"/>.</summary>
        public Point Point { get { return new Point(X, Y); } }
        /// <summary>Red channel of pixel's color.</summary>
        public byte R { get; set; }
        /// <summary>Reserved color value, with no direct function, can be used for 
        /// additional information.</summary>
        public Color Reserved { get; set; }
        /// <summary>Reserved integer value, with no direct function, can be used for 
        /// additional information like an identifier.</summary>
        public int ReservedId { get; set; }
        /// <summary>X-coordinate.</summary>
        public int X { get; set; }
        /// <summary>Y-coordinate.</summary>
        public int Y { get; set; }

        /// <summary>
        /// Determines whether the <paramref name="color"/> is same color.
        /// Alpha channel is irrelevant for evaluating.
        /// </summary>
        /// <param name="color">The color to compare.</param>
        /// <param name="tolerance">The tolerance for allowed variation between
        /// the own color and <paramref name="color"/>.</param>
        /// <returns>
        ///   <c>True</c> if it is the same color; otherwise, <c>false</c>.
        /// </returns>
        public bool IsSameLUVColor(Color color, float tolerance)
        {
            RGB rgb = new RGB(R, G, B);
            ColorTriple luv1 = rgb.To<LUV>().Color;
            rgb = new RGB(color.R, color.G, color.B);
            ColorTriple luv2 = rgb.To<LUV>().Color;

            return Vector.Distance(luv1, luv2) <= tolerance;
        }

        /// <summary>
        /// Determines whether the <paramref name="color"/> is same color. 
        /// Alpha channel is irrelevant for evaluating.
        /// </summary>
        /// <param name="color">The color to compare.</param>
        /// <param name="tolerance">The tolerance for allowed variation between
        /// the own color and <paramref name="color"/>.</param>
        /// <returns><c>True</c> if it is the same color; otherwise, <c>false</c>.
        /// </returns>
        public bool IsSameRGBColor(Color color, Color tolerance)
        {
            if (Math.Abs(R - color.R) <= tolerance.R &&
                Math.Abs(G - color.G) <= tolerance.G &&
                Math.Abs(B - color.B) <= tolerance.B)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Determines whether the <paramref name="pixel"/> has same color. 
        /// Alpha channel is irrelevant for evaluating.
        /// </summary>
        /// <param name="pixel">The pixel to compare.</param>
        /// <param name="tolerance">The tolerance for allowed variation between
        /// the own color and <paramref name="pixel"/>'s color.</param>
        /// <returns><c>True</c> if it is the same color; otherwise, <c>false</c>.
        /// </returns>
        public bool IsSameRGBColor(PixelRGBA pixel, Color tolerance)
        {
            return Math.Abs(R - pixel.R) <= tolerance.R &&
                   Math.Abs(G - pixel.G) <= tolerance.G &&
                   Math.Abs(B - pixel.B) <= tolerance.B;
        }

        /// <summary>
        /// Determines whether the <paramref name="color"/> is same color.
        /// Alpha channel is irrelevant for evaluating.
        /// </summary>
        /// <param name="color">The color to compare.</param>
        /// <param name="tolH">The tolerance for hue channel.</param>
        /// <param name="tolS">The tolerance for saturation channel.</param>
        /// <param name="tolL">The tolerance for lightness channel.</param>
        /// <returns>
        ///   <c>True</c> if it is the same color; otherwise, <c>false</c>.
        /// </returns>
        public bool IsSameHSLColor(Color color, float tolH, float tolS, float tolL)
        {
            HSL hsl1 = HSL.FromRGB(R, G, B);
            HSL hsl2 = HSL.FromRGB(color.R, color.G, color.B);
            return Math.Abs(hsl1.H - hsl2.H) <= tolH &&
                   Math.Abs(hsl1.S - hsl2.S) <= tolS &&
                   Math.Abs(hsl1.L - hsl2.L) <= tolL;
        }

        ///// <summary>
        ///// Determines whether the passed <paramref name="source">pixel</paramref> 
        ///// has same material like this instance.
        ///// </summary>
        ///// <param name="source">The pixel to compare.</param>
        ///// <param name="tolerance">The tolerance values for every triple of 
        ///// specified color space parameter.</param>
        ///// <param name="colorSpace">The specified color space to use for 
        ///// comparing.</param>
        ///// <returns><c>True</c> if it is the same material; otherwise, <c>false</c>.
        ///// </returns>
        //public bool IsSameColorTriple(
        //    PixelRGBA source, ColorTriple tolerance, ColorSpaceEnum colorSpace)
        //{
        //    IColorSpace colorSpaceSource = new RGB(source.R, source.G, source.B);
        //    IColorSpace colorSpaceThis = new RGB(R, G, B);

        //    switch (colorSpace)
        //    {
        //        case ColorSpaceEnum.HSB:
        //            colorSpaceSource = colorSpaceSource.To<HSB>();
        //            colorSpaceThis = colorSpaceThis.To<HSB>();
        //            break;
        //        case ColorSpaceEnum.HSL:
        //            colorSpaceSource = colorSpaceSource.To<HSL>();
        //            colorSpaceThis = colorSpaceThis.To<HSL>();
        //            break;
        //        case ColorSpaceEnum.LAB:
        //            colorSpaceSource = colorSpaceSource.To<LAB>();
        //            colorSpaceThis = colorSpaceThis.To<LAB>();
        //            break;
        //        case ColorSpaceEnum.LCH:
        //            colorSpaceSource = colorSpaceSource.To<LCH>();
        //            colorSpaceThis = colorSpaceThis.To<LCH>();
        //            break;
        //        case ColorSpaceEnum.LUV:
        //            colorSpaceSource = colorSpaceSource.To<LUV>();
        //            colorSpaceThis = colorSpaceThis.To<LUV>();
        //            break;
        //        case ColorSpaceEnum.RGB:
        //            // do nothing here
        //            break;
        //        case ColorSpaceEnum.SRGB:
        //            colorSpaceSource = colorSpaceSource.To<SRGB>();
        //            colorSpaceThis = colorSpaceThis.To<SRGB>();
        //            break;
        //        case ColorSpaceEnum.XYZ:
        //            colorSpaceSource = colorSpaceSource.To<XYZ>();
        //            colorSpaceThis = colorSpaceThis.To<XYZ>();
        //            break;
        //    }

        //    ColorTriple tsrc = colorSpaceSource.Color;
        //    ColorTriple t = colorSpaceThis.Color;

        //    return Math.Abs(t.A - tsrc.A) <= tolerance.A &&
        //           Math.Abs(t.B - tsrc.B) <= tolerance.B &&
        //           Math.Abs(t.C - tsrc.C) <= tolerance.C;
        //}

        ///// <summary>
        ///// Determines whether the passed <paramref name="source">pixel</paramref> 
        ///// has <b>one</b> same color triple like this instance.
        ///// </summary>
        ///// <param name="source">The pixel to compare.</param>
        ///// <param name="tolerance">The tolerance values for every triple of 
        ///// specified color space parameter.</param>
        ///// <param name="colorSpace">The specified color space to use for 
        ///// comparing.</param>
        ///// <returns><c>True</c> if it has <b>one</b> same color triple; 
        ///// otherwise, <c>false</c>.</returns>
        //public bool IsOneColorTripleTheSame(
        //    PixelRGBA source, ColorTriple tolerance, ColorSpaceEnum colorSpace)
        //{
        //    IColorSpace colorSpaceSource = new RGB(source.R, source.G, source.B);
        //    IColorSpace colorSpaceThis = new RGB(R, G, B);

        //    switch (colorSpace)
        //    {
        //        case ColorSpaceEnum.HSB:
        //            colorSpaceSource = colorSpaceSource.To<HSB>();
        //            colorSpaceThis = colorSpaceThis.To<HSB>();
        //            break;
        //        case ColorSpaceEnum.HSL:
        //            colorSpaceSource = colorSpaceSource.To<HSL>();
        //            colorSpaceThis = colorSpaceThis.To<HSL>();
        //            break;
        //        case ColorSpaceEnum.LAB:
        //            colorSpaceSource = colorSpaceSource.To<LAB>();
        //            colorSpaceThis = colorSpaceThis.To<LAB>();
        //            break;
        //        case ColorSpaceEnum.LCH:
        //            colorSpaceSource = colorSpaceSource.To<LCH>();
        //            colorSpaceThis = colorSpaceThis.To<LCH>();
        //            break;
        //        case ColorSpaceEnum.LUV:
        //            colorSpaceSource = colorSpaceSource.To<LUV>();
        //            colorSpaceThis = colorSpaceThis.To<LUV>();
        //            break;
        //        case ColorSpaceEnum.RGB:
        //            // do nothing here
        //            break;
        //        case ColorSpaceEnum.SRGB:
        //            colorSpaceSource = colorSpaceSource.To<SRGB>();
        //            colorSpaceThis = colorSpaceThis.To<SRGB>();
        //            break;
        //        case ColorSpaceEnum.XYZ:
        //            colorSpaceSource = colorSpaceSource.To<XYZ>();
        //            colorSpaceThis = colorSpaceThis.To<XYZ>();
        //            break;
        //    }

        //    ColorTriple tsrc = colorSpaceSource.Color;
        //    ColorTriple t = colorSpaceThis.Color;

        //    return  Math.Abs(t.A - tsrc.A) <= tolerance.A ||
        //            Math.Abs(t.B - tsrc.B) <= tolerance.B ||
        //            Math.Abs(t.C - tsrc.C) <= tolerance.C;
        //}

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="pixel">The original <see cref="PixelRGBA"/>.</param>
        /// <param name="size">The size to move the coordinates.</param>
        /// <returns>The resulting <see cref="PixelRGBA"/> with new coordinates.
        /// </returns>
        public static PixelRGBA operator +(PixelRGBA pixel, Size size)
        {
            return new PixelRGBA
                       {
                           A = pixel.A,
                           B = pixel.B,
                           G = pixel.G,
                           R = pixel.R,
                           Reserved = pixel.Reserved,
                           ReservedId = pixel.ReservedId,
                           X = pixel.X + size.Width,
                           Y = pixel.Y + size.Height
                       };
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="pixel">The original <see cref="PixelRGBA"/>.</param>
        /// <param name="size">The size to move the coordinates.</param>
        /// <returns>The resulting <see cref="PixelRGBA"/> with new coordinates.
        /// </returns>
        public static PixelRGBA operator -(PixelRGBA pixel, Size size)
        {
            return new PixelRGBA
                       {
                           A = pixel.A,
                           B = pixel.B,
                           G = pixel.G,
                           R = pixel.R,
                           Reserved = pixel.Reserved,
                           ReservedId = pixel.ReservedId,
                           X = pixel.X - size.Width,
                           Y = pixel.Y - size.Height
                       };
        }
    }
}
